package network.t_01_bio;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * https://www.liaoxuefeng.com/wiki/1252599548343744/1305207629676577
 * https://www.cnblogs.com/jhxxb/p/11303947.html
 * 当一个线程调用 read() 或 write() 时，该线程被阻塞，直到有一些数据被读取或写入，该线程在此期间不能执行其他任务。
 * 因此，在网络通信进行 IO 操作时，由于线程会阻塞，所以服务器端必须为每个客户端都提供一个独立的线程进行处理，当服务器端需要处理大量客户端时，性能急剧下降。
 * <p>
 * telnet 127.0.0.1 6666
 * ctrl + ]
 * send msg
 */
public class BIOServer {
    static ExecutorService threadPool = Executors.newFixedThreadPool(10);

    public static void main(String[] args) {
        try (ServerSocket serverSocket = new ServerSocket(6666)) {
            // serverSocket.bind(new InetSocketAddress("127.0.0.1", 6666));
            while (true) {
                Socket socket = serverSocket.accept(); // 阻塞当前线程，等待客户端连接
                threadPool.execute(() -> handler3(socket)); // 创建一个线程与之通讯
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    static void handler1(Socket socket) {
        try (InputStream is = socket.getInputStream(); // 通过 socket 获取输入流
             OutputStream os = socket.getOutputStream(); // 通过 socket 获取输出流
             ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            int length;
            byte[] buffer = new byte[4096];
            while ((length = is.read(buffer)) != -1) baos.write(buffer, 0, length); // 阻塞当前线程，等待对方发送数据
            System.out.println(baos.toString(StandardCharsets.UTF_8.name()) + "\n收到数据");
            socket.shutdownInput();

            os.write(baos.toByteArray());
            os.flush();
            socket.shutdownOutput();
            System.out.println("发送完毕");
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    static void handler2(Socket socket) {
        try (InputStream is = socket.getInputStream();
             OutputStream os = socket.getOutputStream();
             ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            int length;
            byte[] buffer = new byte[4096];
            while (keepalive(socket)) {
                while ((length = is.read(buffer)) != -1) { // 阻塞当前线程，等待客户端发送数据
                    baos.write(buffer, 0, length);
                    if (buffer[length - 1] == '\n') break;
                }
                System.out.println(baos.toString(StandardCharsets.UTF_8.name()) + "\n收到数据");

                os.write(baos.toByteArray());
                os.flush();
                System.out.println("发送完毕");
                baos.reset();
            }
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    static void handler3(Socket socket) {
        try (DataInputStream dis = new DataInputStream(socket.getInputStream());
             DataOutputStream dos = new DataOutputStream(socket.getOutputStream())) {
            while (keepalive(socket)) {
                String tag = dis.readUTF();
                byte[] bytes = new byte[dis.readInt()];
                dis.readFully(bytes);
                System.out.println(new String(bytes, StandardCharsets.UTF_8) + "\n收到数据");

                dos.writeUTF(tag);
                dos.writeInt(bytes.length);
                dos.write(bytes);
                dos.flush();
                System.out.println("发送完毕！");
            }
            socket.close();
            System.out.printf("%s，%s\n", Thread.currentThread().getName(), "已关闭");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    static boolean keepalive(Socket socket) {
        try {
            socket.sendUrgentData(0xFF);
        } catch (IOException e) {
            return false;
        }
        return true;
    }
}